package com.service.socket.framework.log;

import com.service.utility.common.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author losi
 */
@Slf4j
@Aspect
@Component
public class RequestLogAspect {

    private String uuid;

    @Pointcut("@annotation(com.service.socket.framework.log.RequestLog)")
    public void requestLogPointCut() {
    }


    @Around("requestLogPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        final long before = System.currentTimeMillis();
        final Signature signature = joinPoint.getSignature();
        if (signature == null) {
            return joinPoint.proceed();
        }
        final Method method = ((MethodSignature) signature).getMethod();
        final RequestLog anno = method.getAnnotation(RequestLog.class);
        if (anno == null) {
            return joinPoint.proceed();
        }
        final Class[] exclude = anno.exclude();
        final String value = anno.value();
        final String className = joinPoint.getTarget().getClass().getName();
        final String methodName = signature.getName();
        final Object[] args = joinPoint.getArgs();
        final String[] parameterNames = ((MethodSignature) signature).getParameterNames();
        final Class[] parameterTypes = ((MethodSignature) signature).getParameterTypes();
        final StringBuilder sb = new StringBuilder();
        if (!ArrayUtils.isEmpty(parameterTypes)) {
            final boolean hasExclude = !ArrayUtils.isEmpty(exclude);
            Class clazz;
            final int length = parameterTypes.length;
            for (int i = 0; i < length; i++) {
                clazz = parameterTypes[i];
                if (hasExclude && inExclude(clazz, exclude)) {
                    continue;
                }
                sb.append(parameterNames[i]).append(": ").append(args[i]);
                if (i != length - 1) {
                    sb.append(", ");
                }
            }
        }
        uuid = UUIDUtil.randomUUID();
        if (StringUtils.isNotBlank(value)) {
            log.info("操作:{} 请求开始 入参:{} UUID:{}", value, sb.toString(), uuid);
        } else {
            log.info("操作:{} 请求开始 入参:{} UUID:{}", className + methodName, sb.toString(), uuid);
        }
        Object process = joinPoint.proceed();
        final long after = System.currentTimeMillis();
        if (StringUtils.isNotBlank(value)) {
            log.info("操作:{} 请求结束 UUID:{} 耗时:{}ms", value, uuid, after - before);
        } else {
            log.info("操作:{} 请求结束 UUID:{} 耗时:{}ms", className + methodName, uuid, after - before);
        }
        return process;
    }

    private boolean inExclude(Class type, Class[] exclude) {
        for (Class clazz : exclude) {
            if (clazz == type) return true;
        }
        return false;
    }
}
